package com.niiwoo.civet.account.service.local.bill;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.niiwoo.civet.account.dao.entity.NiiwooBill;
import com.niiwoo.civet.account.dao.entity.NiiwooBillExample;
import com.niiwoo.civet.account.dao.entity.NiiwooBillExampleExt;
import com.niiwoo.civet.account.dao.entity.UserBillBaseExample;
import com.niiwoo.civet.account.dao.mapper.NiiwooBillMapper;
import com.niiwoo.civet.account.dao.mapper.NiiwooBillMapperExt;
import com.niiwoo.civet.account.dao.mapper.UserBillBaseMapperExt;
import com.niiwoo.civet.account.dto.bill.BillBase;
import com.niiwoo.civet.account.dto.bill.GroupBillAmountBySecondSubjectResponeDTO;
import com.niiwoo.civet.account.dto.request.BillQueryRequest;
import com.niiwoo.civet.account.dto.request.MonthBillRequestDTO;
import com.niiwoo.civet.account.dto.request.UserBillQueryRequest;
import com.niiwoo.civet.account.dto.response.MonthBillCountResponseDTO;
import com.niiwoo.civet.account.dto.response.MonthBillResponseDTO;
import com.niiwoo.civet.account.dto.response.UserAccountAbnormalStatisticsRespDTO;
import com.niiwoo.civet.account.enums.*;
import com.niiwoo.civet.base.enums.ChannelCodeEnum;
import com.niiwoo.tripod.base.utils.DateUtils;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.assertj.core.util.Strings;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.niiwoo.civet.account.enums.BillFirstSubjectEnum.*;


/**
 * 用户、普通机构的账单Service
 *
 * @author xiaoyingping
 * @create 2017-11-20 10:06
 **/
@Service
@Slf4j
public class NiiwooBillService {
    @Autowired
    private NiiwooBillMapper niiwooBillMapper;
    @Autowired
    private NiiwooBillMapperExt niiwooBillMapperExt;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private UserBillBaseMapperExt userBillBaseMapperExt;


    /**
     * 新增用户账单
     *
     * @param niiwooBill
     * @return
     */
    public int addNiiwooBill(BillBase billBase, NiiwooBill niiwooBill) {
        niiwooBill.setBillId(snowflakeIdWorker.nextId());
        niiwooBill.setBillBaseId(billBase.getId());
        niiwooBill.setBillType(billBase.getBillType());
        niiwooBill.setUserId(billBase.getUserId());
        niiwooBill.setOrgId(billBase.getOrgId());
        niiwooBill.setAccountId(billBase.getAccountId());
        if(Objects.isNull(niiwooBill.getInvestId())){
            niiwooBill.setInvestId(billBase.getInvestId());
        }
        niiwooBill.setGuaranteeId(billBase.getGuaranteeId());
        if(Strings.isNullOrEmpty(niiwooBill.getOrderId())){
            niiwooBill.setOrderId(billBase.getOrderId());
        }
        if(Objects.isNull(niiwooBill.getOrderType())){
            niiwooBill.setOrderType(billBase.getOrderType());
        }
        if(Objects.isNull(niiwooBill.getStatus())){
            niiwooBill.setStatus(billBase.getStatus());
        }
        niiwooBill.setProjectPackageId(billBase.getProjectPackageId());
        niiwooBill.setCreateTime(billBase.getCreateTime());
        niiwooBill.setUpdateTime(billBase.getUpdateTime());
        niiwooBill.setYearMonth(new DateTime(billBase.getCreateTime()).toString("yyyyMM"));

        BillSecondSubjectEnum secondSubjectEnum = BillSecondSubjectEnum.getEnumByCode(niiwooBill.getSecondSortCode());
        niiwooBill.setFirstSortCode(secondSubjectEnum.getFirstSubject().getCode());
        niiwooBill.setInoutType(secondSubjectEnum.getFirstSubject().getInOutType());
        niiwooBill.setBillTitle(secondSubjectEnum.getCodeName());

        return niiwooBillMapper.insertSelective(niiwooBill);
    }

    /**
     * 查询用户账单列表
     *
     * @param billQueryRequest
     * @return
     */
    public List<NiiwooBill> listNiiwooBills(BillQueryRequest billQueryRequest) {
        NiiwooBillExampleExt niiwooBillExample = generateNiiwooBillExample(billQueryRequest);
        niiwooBillExample.setOrderByClause("create_time desc");

        return niiwooBillMapper.selectByExample(niiwooBillExample);
    }

    /**
     * 【分页】查询用户账单列表
     *
     * @param billQueryRequest
     * @return
     */
    public PageInfo<NiiwooBill> pageNiiwooBills(BillQueryRequest billQueryRequest) {
        NiiwooBillExampleExt example = generateNiiwooBillExample(billQueryRequest);
        example.setOrderByClause("create_time desc");

        PageHelper.startPage(billQueryRequest.getPageNumber(), billQueryRequest.getPageSize());
        List<NiiwooBill> niiwooBillList = niiwooBillMapper.selectByExample(example);
        PageInfo<NiiwooBill> pageInfo = new PageInfo(niiwooBillList);

        return pageInfo;
    }

    /**
     * 【分页】查询用户机构账单列表（后台）
     *
     * @param userBillQueryRequest
     * @return
     */
    public List<NiiwooBill> pageNiiwooBills(UserBillQueryRequest userBillQueryRequest) {
        NiiwooBillExampleExt niiwooBillExample = generateNiiwooUserBillExample(userBillQueryRequest);
        if (null == niiwooBillExample) {
            return Collections.EMPTY_LIST;
        }

        //分页条件
        Integer offset = userBillQueryRequest.getOffset();
        Integer pageSize = userBillQueryRequest.getPageSize();
        Assert.notNull(offset, "offset不能为空");
        Assert.notNull(pageSize, "pageSize不能为0");
        niiwooBillExample.setOffset(offset);
        niiwooBillExample.setPageSize(pageSize);
        niiwooBillExample.setOrderByClause("create_time desc,bill_id desc");

        return niiwooBillMapperExt.pageByExample(niiwooBillExample);
    }

    /**
     * 统计账单数量(后台)
     *
     * @param billQueryRequest
     * @return
     */
    public Long countUserNiiwooBills(UserBillQueryRequest billQueryRequest) {
        NiiwooBillExampleExt niiwooBillExample = generateNiiwooUserBillExample(billQueryRequest);
        if (null == niiwooBillExample) {
            return 0L;
        }

        return niiwooBillMapperExt.countByExample(niiwooBillExample);
    }

    /**
     * 按项目包分组并查询每个项目包最新的账单ID【分页】
     *
     * @param userId
     * @param pageNumber
     * @param pageSize
     * @return
     */
    public PageInfo<Long> listLatestBillIdGroupByPackage(String userId, int pageNumber, int pageSize, List<String> firstCodes) {
        Assert.hasLength(userId, "userId不能为空");
        Assert.notNull(pageNumber, "pageNumber不能为空");
        Assert.notNull(pageSize, "pageSize不能为0");

        PageHelper.startPage(pageNumber, pageSize);
        List<Long> billIds = niiwooBillMapperExt.listLatestBillIdGroupByPackage(userId, firstCodes);
        PageInfo<Long> pageInfo = new PageInfo(billIds);

        return pageInfo;
    }


    /**
     * 修改账单状态：通过用户ID和订单ID
     *
     * @param userId     用户ID
     * @param orderId    交易订单ID
     * @param orderType    交易订单类型枚举
     * @param statusEnum 修改后的状态
     */
    public int updateBillStatusByOrderId(String userId, String orderId, OrderTypeEnum orderType, BillStatusEnum statusEnum) {
        Assert.hasLength(userId, "userId不能为空");
        Assert.hasLength(orderId, "orderId不能为空");
        Assert.notNull(orderType, "orderType不能为空");

        NiiwooBillExample example = new NiiwooBillExample();
        NiiwooBillExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        criteria.andOrderIdEqualTo(orderId);
        criteria.andOrderTypeEqualTo(orderType.getCode());

        NiiwooBill niiwooBill = new NiiwooBill();
        niiwooBill.setStatus(statusEnum.getValue());
        niiwooBill.setUpdateTime(new Date());
        return niiwooBillMapper.updateByExampleSelective(niiwooBill, example);
    }

    /**
     * 查询账单列表：通过订单ID和订单类型
     * @param orderId
     * @param orderTypeEnum
     * @return
     */
    public List<NiiwooBill> getByOrderIdAndType(String userId, String orderId, OrderTypeEnum orderTypeEnum){
        Assert.hasLength(userId, "userId不能为空");
        Assert.hasLength(orderId, "orderId不能为空");

        NiiwooBillExample example = new NiiwooBillExample();
        NiiwooBillExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        criteria.andOrderIdEqualTo(orderId);
        if(Objects.nonNull(orderTypeEnum)){
            criteria.andOrderTypeEqualTo(orderTypeEnum.getCode());
        }

        return niiwooBillMapper.selectByExample(example);
    }


    public List<MonthBillCountResponseDTO> listMonthBillIncomeByUserId(String userId) {
        Assert.hasLength(userId, "userId不能为空");
        List<String> firstSortCodes = Arrays.asList(USER_INVEST_REPAYMENT.getCode(), BillFirstSubjectEnum.USER_GUARANTEE_INCOME.getCode(), BillFirstSubjectEnum.USER_GUARANTEE_PAY_BACK.getCode(), BillFirstSubjectEnum.USER_RED.getCode(), USER_PLATFORM_GIVEN.getCode(), USER_SUBSIDY.getCode());
        List<String> notSecondSortCodes = Arrays.asList(BillSecondSubjectEnum.USER_INVEST_REPAYMENT_CORPUS.getCode(), BillSecondSubjectEnum.USER_GUARANTEE_PAY_BACK_CORPUS.getCode());
        MonthBillRequestDTO requestDTO = new MonthBillRequestDTO();
        requestDTO.setUserId(userId);
        requestDTO.setFirstSortCodes(firstSortCodes);
        requestDTO.setSecondSortCodes(notSecondSortCodes);

        return niiwooBillMapperExt.listMonthBillIncomeByUserId(requestDTO);
    }


    public List<NiiwooBill> listNiiwooBillsByBaseId(Long billBaseId, String userId){
        BillQueryRequest billQueryRequest = new BillQueryRequest();
        billQueryRequest.setBillBaseId(billBaseId);
        billQueryRequest.setUserId(userId);
        return this.listNiiwooBills(billQueryRequest);
    }


    private NiiwooBillExampleExt generateNiiwooBillExample(BillQueryRequest billQueryRequest) {
        Assert.notNull(billQueryRequest, "billQueryRequest不能为空");
        String userId = billQueryRequest.getUserId();
        Assert.hasLength(userId, "userId不能为空");

        NiiwooBillExampleExt niiwooBillExample = new NiiwooBillExampleExt();
        NiiwooBillExample.Criteria criteria = niiwooBillExample.createCriteria();
        criteria.andUserIdEqualTo(userId);
        if (Objects.nonNull(billQueryRequest.getBillId())) {
            criteria.andBillIdEqualTo(billQueryRequest.getBillId());
        }
        if (StringUtils.isNotEmpty(billQueryRequest.getOrderId())) {
            criteria.andOrderIdEqualTo(billQueryRequest.getOrderId());
        }
        if (Objects.nonNull(billQueryRequest.getBillBaseId())) {
            criteria.andBillBaseIdEqualTo(billQueryRequest.getBillBaseId());
        }
        if (Objects.nonNull(billQueryRequest.getProjectId())) {
            criteria.andProjectIdEqualTo(billQueryRequest.getProjectId());
        }
        if (Objects.nonNull(billQueryRequest.getProjectPackageId())) {
            criteria.andProjectPackageIdEqualTo(billQueryRequest.getProjectPackageId());
        }
        if (Objects.nonNull(billQueryRequest.getInvestId())) {
            criteria.andInvestIdEqualTo(billQueryRequest.getInvestId());
        }
        if (Objects.nonNull(billQueryRequest.getInoutType())) {
            criteria.andInoutTypeEqualTo(billQueryRequest.getInoutType().getType());
        }
        if (Objects.nonNull(billQueryRequest.getBillFirstSubjectEnum())) {
            criteria.andFirstSortCodeEqualTo(billQueryRequest.getBillFirstSubjectEnum().getCode());
        }
        if (Objects.nonNull(billQueryRequest.getStatus())) {
            criteria.andStatusEqualTo(billQueryRequest.getStatus());
        }
        if (Objects.nonNull(billQueryRequest.getFirstCodeS())) {
            criteria.andFirstSortCodeIn(billQueryRequest.getFirstCodeS());
        }
        if (Objects.nonNull(billQueryRequest.getSecondCodes())) {
            criteria.andSecondSortCodeIn(billQueryRequest.getSecondCodes());
        }
        if (Objects.nonNull(billQueryRequest.getIsVisibleEnum())) {
            criteria.andIsVisibleEqualTo(billQueryRequest.getIsVisibleEnum().getValue());
        }
        if (Objects.nonNull(billQueryRequest.getMinBillTime())) {
            criteria.andCreateTimeGreaterThanOrEqualTo(billQueryRequest.getMinBillTime());
        }
        if (Objects.nonNull(billQueryRequest.getMaxBillTime())) {
            criteria.andCreateTimeLessThanOrEqualTo(billQueryRequest.getMaxBillTime());
        }

        return niiwooBillExample;
    }

    private NiiwooBillExampleExt generateNiiwooUserBillExample(UserBillQueryRequest userBillQueryRequest) {
        Assert.notNull(userBillQueryRequest, "billQueryRequest不能为空");
        List<String> userIds = userBillQueryRequest.getUserIds();
        Assert.notEmpty(userIds, "userIds不能为空");

        NiiwooBillExampleExt niiwooBillExample = new NiiwooBillExampleExt();
        NiiwooBillExample.Criteria criteria = niiwooBillExample.createCriteria();
        if (userIds.size() > 1) {
            criteria.andUserIdIn(userIds);
        } else {
            criteria.andUserIdEqualTo(userIds.get(0));
        }


        if (userBillQueryRequest.getStartCreateDate() != null && userBillQueryRequest.getEndCreateDate() != null) {
            criteria.andCreateTimeBetween(userBillQueryRequest.getStartCreateDate(), userBillQueryRequest.getEndCreateDate());
        }
        if (Objects.nonNull(userBillQueryRequest.getProjectId())) {
            criteria.andProjectIdEqualTo(userBillQueryRequest.getProjectId());
        }
        if (Objects.nonNull(userBillQueryRequest.getRole())) {
            criteria.andBillTypeEqualTo(userBillQueryRequest.getRole().getCode());
        }
        if (Objects.nonNull(userBillQueryRequest.getChannelCode())) {
            criteria.andChannelCodeEqualTo(userBillQueryRequest.getChannelCode());
        }else{
            criteria.andChannelCodeNotEqualTo(ChannelCodeEnum.KAOLA.getChanelCode());
        }
        if (Objects.nonNull(userBillQueryRequest.getInoutType())) {
            criteria.andInoutTypeEqualTo(userBillQueryRequest.getInoutType().getType());
        }
        if (Objects.nonNull(userBillQueryRequest.getFirstSortCode())) {
            criteria.andFirstSortCodeEqualTo(userBillQueryRequest.getFirstSortCode());
        }
        if (Objects.nonNull(userBillQueryRequest.getSecondSortCode())) {
            criteria.andSecondSortCodeEqualTo(userBillQueryRequest.getSecondSortCode());
        }
        if (Objects.nonNull(userBillQueryRequest.getProjectType())) {
            criteria.andProjectTypeEqualTo(userBillQueryRequest.getProjectType());
        }
        if (Objects.nonNull(userBillQueryRequest.getIsVisibleEnum())) {
            criteria.andIsVisibleEqualTo(userBillQueryRequest.getIsVisibleEnum().getValue());
        }
        if (Objects.nonNull(userBillQueryRequest.getBillBaseId())) {
            criteria.andBillBaseIdEqualTo(userBillQueryRequest.getBillBaseId());
        }


        return niiwooBillExample;
    }

    public MonthBillCountResponseDTO getMonthBillSum(MonthBillRequestDTO requestDTO) {
        MonthBillCountResponseDTO responseDTO = niiwooBillMapperExt.countTotalMonthAmountByCondition(requestDTO);
        if (Objects.isNull(responseDTO)) return null;
        return responseDTO;
    }

    /**
     * 根据BillBaseId查询明细列表
     * @param userId
     * @param billBaseId
     * @return
     */
    public List<NiiwooBill> getCheckAccountBillsByBaseId(String userId, Long billBaseId) {
        Assert.hasLength(userId, "userId不能为空");
        Assert.notNull(billBaseId,"billBaseId不能为空");

        NiiwooBillExample niiwooBillExample = new NiiwooBillExample();
        NiiwooBillExample.Criteria criteria = niiwooBillExample.createCriteria();
        criteria.andUserIdEqualTo(userId);
        criteria.andBillBaseIdEqualTo(billBaseId);
        return niiwooBillMapperExt.selectByExample(niiwooBillExample);
    }

    /**
     * 通过给定的ID获取 用户的异常统计数据
     *
     * @return
     */
    public UserAccountAbnormalStatisticsRespDTO findUserAmountByUserId(String userId) {
        UserAccountAbnormalStatisticsRespDTO userAccountAbnormalStatistics = new UserAccountAbnormalStatisticsRespDTO();
        // 实际收入
        BigDecimal totalActualIncomeMoney = niiwooBillMapperExt.sumAmountByUserIdAndSecondSortCode(userId, BillSecondSubjectEnum.USER_OTHER_IN_ACTUALLY.getCode());
        //实际支出
        BigDecimal totalActualExpendMoney = niiwooBillMapperExt.sumAmountByUserIdAndSecondSortCode(userId, BillSecondSubjectEnum.USER_OTHER_OUT_REAL.getCode());
        //走账收入
        BigDecimal totalGoAccountIncomeMoney = niiwooBillMapperExt.sumAmountByUserIdAndSecondSortCode(userId, BillSecondSubjectEnum.USER_OTHER_IN.getCode());
        //走账支出
        BigDecimal totalGoAccountExpendMoney = niiwooBillMapperExt.sumAmountByUserIdAndSecondSortCode(userId, BillSecondSubjectEnum.USER_OTHER_OUT_INVEST.getCode());

        userAccountAbnormalStatistics.setTotalActualExpendMoney(totalActualExpendMoney);
        userAccountAbnormalStatistics.setTotalActualIncomeMoney(totalActualIncomeMoney);
        userAccountAbnormalStatistics.setTotalActualMoney(totalActualIncomeMoney.subtract(totalActualExpendMoney));
        userAccountAbnormalStatistics.setTotalGoAccountExpendMoney(totalGoAccountExpendMoney);
        userAccountAbnormalStatistics.setTotalGoAccountIncomeMoney(totalGoAccountIncomeMoney);
        userAccountAbnormalStatistics.setTotalGoAccountMoney(totalGoAccountIncomeMoney.subtract(totalGoAccountExpendMoney));

        return userAccountAbnormalStatistics;
    }


    /**
     * 统计用户账单金额
     * @param userId
     * @param amountType
     * @return
     */
    public BigDecimal statisticalUserBillAmount(String userId, StatisticalUserBillAmountTypeEnum amountType){
        NiiwooBillExample example = new NiiwooBillExample();
        NiiwooBillExample.Criteria criteria = example.createCriteria();
        criteria.andUserIdEqualTo(userId);
        if(StatisticalUserBillAmountTypeEnum.RAISE_INTEREST_RATE_COUPONS == amountType){
            criteria.andSecondSortCodeEqualTo(BillSecondSubjectEnum.USER_RED_COUPON_RATE_INTEREST.getCode());
        }else if(StatisticalUserBillAmountTypeEnum.INVESTMENT_CASHBACK == amountType){
            criteria.andSecondSortCodeEqualTo(BillSecondSubjectEnum.USER_RED_INVEST.getCode());
        }else if(StatisticalUserBillAmountTypeEnum.INVESTMENT_RAISE_INTEREST == amountType){
            criteria.andSecondSortCodeEqualTo(BillSecondSubjectEnum.USER_PLATFORM_GIVEN_ACTIVITY_RAISE_INTEREST.getCode());
        }else {
            throw new BizException("");
        }

        return niiwooBillMapperExt.sumAmountByExample(example);
    }

    /**
     * 统计用户的月账单
     * @param userId
     * @param yearMonthBill
     * @return
     */
    public MonthBillResponseDTO statisticalUserMonthBill(String userId,String yearMonthBill){
        //时间处理
        DateTime yearMonthDateTime = new DateTime(DateUtils.parseDateFromString(yearMonthBill, "yyyyMM"));
        DateTime startDateTime = yearMonthDateTime.withDayOfMonth(1).withTimeAtStartOfDay();//月初第一天
        DateTime endDateTime = startDateTime.plusMonths(1).minusSeconds(1);//月末最后一天

        //分组统计账单金额
        NiiwooBillExample staticsExample = new NiiwooBillExample();
        NiiwooBillExample.Criteria staticsCriteria = staticsExample.createCriteria();
        staticsCriteria.andUserIdEqualTo(userId);
        staticsCriteria.andChannelCodeEqualTo(ChannelCodeEnum.APP.getChanelCode());
        staticsCriteria.andStatusEqualTo(BillStatusEnum.SUCCESS.getValue());
        staticsCriteria.andCreateTimeBetween(startDateTime.toDate(), endDateTime.toDate());
        List<GroupBillAmountBySecondSubjectResponeDTO> groupBillAmountResponeDTOList = this.groupAmountBySecondSubject(staticsExample);

        //回款本金
        MonthBillResponseDTO monthBillResponseDTO = new MonthBillResponseDTO();
        List<String> secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.RECEIVED_CAPITAL);
        BigDecimal billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setReceivablePrincipalAmount(billAmount);

        //回款利息 + 罚息
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.RECEIVED_PAYMENT_INTEREST);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setReceivableInterest(billAmount);

        //卡券红包
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.CARD_AND_REDPACKET);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setCouponReward(billAmount);

        //平台奖励
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.PLATFORM_PRIZE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setPlateformReward(billAmount);

        //推荐奖励
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.RECOMMEND_PRIZE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setRecommendReward(billAmount);

        //借款金额
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.LOAN_AMOUNT);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setBorrowAmount(billAmount);

        //还款本金
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.REPAYMENT_CAPITAL);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setRefundPrincipalAmount(billAmount);

        //还款利息
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.REPAYMENT_INTEREST);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setRefundInterest(billAmount);

        //还款罚息
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.REPAYMENT_PENALTY);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setRefundPenalty(billAmount);

        //充值金额
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.RECHARGE_AMOUNT);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setRechargeAmount(billAmount);

        //提现金额
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.WITHDRAW_AMOUNT);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setWithdrawAmount(billAmount);

        //提现手续费
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.WITHDRAW_FEE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setWithdrawFee(billAmount);

        //转让手续费
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.TRANSFER_FEE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setTransferFee(billAmount);

        //投资费用
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.INVEST_FEE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setInvestFee(billAmount);

        //借款费用
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.LOAN_FEE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setLoanFee(billAmount);

        //担保费用
        secondSubjects = BillAmountStatisticsEnum.getBillSecondSubjects(BillAmountStatisticsEnum.GUARANTEE_FEE);
        billAmount = this.getAmountFromGroupBySecondSubject(groupBillAmountResponeDTOList, secondSubjects);
        monthBillResponseDTO.setGuaranteeFee(billAmount);


        //投资金额、投资笔数
        ExecutorService executor = Executors.newWorkStealingPool(5);
        List<Callable<Boolean>> callables = new ArrayList<>();
        Callable invest = new Callable() {
            @Override
            public Boolean call() throws Exception {
                { //投资金额
                    NiiwooBillExample example = new NiiwooBillExample();
                    NiiwooBillExample.Criteria criteria = example.createCriteria();
                    criteria.andUserIdEqualTo(userId);
                    criteria.andStatusEqualTo(BillStatusEnum.SUCCESS.getValue());
                    criteria.andChannelCodeEqualTo(ChannelCodeEnum.APP.getChanelCode());
                    criteria.andSecondSortCodeEqualTo(BillSecondSubjectEnum.USER_INVEST.getCode());
                    criteria.andInoutTypeEqualTo(InoutTypeEnum.OUT_TYPE.getType());
                    criteria.andCreateTimeBetween(startDateTime.toDate(),endDateTime.toDate());
                    BigDecimal result = niiwooBillMapperExt.sumAmountByExample(example);
                    monthBillResponseDTO.setInvestAmount(result);
                    log.info("用户:{},投资金额:{},统计完成,Thread-Id:{},Thread-Name{}",userId,result,Thread.currentThread().getId(),Thread.currentThread().getName());
                }
                { //投资笔数
                    UserBillBaseExample example = new UserBillBaseExample();
                    UserBillBaseExample.Criteria criteria = example.createCriteria();
                    criteria.andUserIdEqualTo(userId);
                    criteria.andOrderTypeIn(Arrays.asList(OrderTypeEnum.INVEST.getCode(),OrderTypeEnum.ZNT_INVEST.getCode()));
                    criteria.andInoutTypeEqualTo(InoutTypeEnum.OUT_TYPE.getType());
                    criteria.andStatusEqualTo(BillStatusEnum.SUCCESS.getValue());
                    criteria.andChannelCodeEqualTo(ChannelCodeEnum.APP.getChanelCode());
                    criteria.andCreateTimeBetween(startDateTime.toDate(),endDateTime.toDate());
                    int result = userBillBaseMapperExt.countByExample(example);
                    monthBillResponseDTO.setInvestCount(result);
                    log.info("用户:{},投资笔数:{},统计完成,Thread-Id:{},Thread-Name{}",userId,result,Thread.currentThread().getId(),Thread.currentThread().getName());
                }
                return Boolean.TRUE;
            }
        };


        callables.add(invest);
        try {
            executor.invokeAll(callables)
                    .stream()
                    .map(future -> {
                        try {
                            boolean lsResult = (Boolean)future.get();
                            if( ! lsResult ){
                                throw new BizException("系统异常");
                            }
                            return future.get();
                        }
                        catch (Exception e) {
                            throw new IllegalStateException(e);
                        }
                    });
            executor.shutdown();
        } catch (InterruptedException e) {
            log.error("月账单统计异常：",e);
        }

        return monthBillResponseDTO;
    }



    /**
     * 按账单二级科目分组统计金额
     * @param example
     * @return
     */
    public List<GroupBillAmountBySecondSubjectResponeDTO> groupAmountBySecondSubject(NiiwooBillExample example){
        long startTime = System.currentTimeMillis();
        List<GroupBillAmountBySecondSubjectResponeDTO> list = niiwooBillMapperExt.groupAmountBySecondSubject(example);
        log.info("groupAmountBySecondSubject costTime:{} ms, result:{}", System.currentTimeMillis() - startTime, JSON.toJSONString(list));
        return list;
    }


    private BigDecimal getAmountFromGroupBySecondSubject (List<GroupBillAmountBySecondSubjectResponeDTO> billAmountDTOList, List<String> secondSubjects){
        BigDecimal totalAmount = billAmountDTOList.stream()
                .filter(e -> secondSubjects.contains(e.getBillSecondSubject()))
                .map(e -> e.getAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if(Objects.isNull(totalAmount)){
            totalAmount = BigDecimal.ZERO;
        }

        return totalAmount;
    }
}
